#include "boot_config.h"
#include "flash.h"
//#include "xon_xoff.h"
//#include "uart_api.h"
#include "boot.h"
#include "common_api.h"


/*
boot_info load_boot_info;
boot_info *p_boot_info = &load_boot_info;

uint32_t bootloader_load(void * vStart) {
    
    // Local variables
    uint8_t page[MEMORY_PAGE_SIZE];
    uint32_t bytes;
    uint8_t * pCurrent;
    uint32_t code_len = 0;
    
    // Initialize address and notify host
    pCurrent = (uint8_t *) vStart;
//    communication_ready();//wait communication ready
    
//    UART_send_string("Bootloader: Upgrading");
    do {
        // Receive a page
//        bytes = communication_receive(page);
//        UART_send_string(".");
#ifdef USE_MEMORY_PARTITIONING
        // Check for an overflow
        if (((unsigned int) pCurrent + bytes - (unsigned int) vStart) >
            (unsigned int) REGION_SIZE) 
        {
//            UART_send_string("Partitioning: Firmware too big.\n\r");
            return 0;
        }
#endif
        
        // End of firmware ?
        if (bytes > 0) {
            
            // Pad page with 0xFF and write it
            while (bytes < MEMORY_PAGE_SIZE) {
                
                page[bytes] = 0xFF;
                bytes++;
            }
            
            flash_write(pCurrent, page);
            
            // Loop progression
            pCurrent += MEMORY_PAGE_SIZE;
            code_len += MEMORY_PAGE_SIZE;
        }
    } while (bytes > 0);
    
//    UART_send_string("\n\r");
//    UART_send_string("Bootloader: Loading done.\n\r");
    
    return (code_len);
}

void region_load(void *vBufStart) 
{       
    // Load firmware in region and compute its length    
    p_boot_info->boot_len = bootloader_load(vBufStart); 
}

void read_bootInfo(void *vAddress, boot_info *pBootInfo)
{
    boot_info *pAddr = (boot_info *)vAddress;
    
    pBootInfo->boot_v = pAddr->boot_v;
    pBootInfo->boot_len = pAddr->boot_len;
    pBootInfo->boot_crc32 = pAddr->boot_crc32;
}

void write_bootInfo(void *vAddress, boot_info *pBootInfo)
{
    flash_write(vAddress,(void *)pBootInfo);
}


static unsigned int bootCode_check(unsigned int boot_crc32_a, unsigned int boot_crc32_b) 
{
    if (boot_crc32_a == boot_crc32_b) 
    {
        return OK;
    }
    else 
    {
        return ERROR;
    }
}

unsigned int bootCode_isValid(void *vAddress,
                                        unsigned int boot_crc32,
                                        unsigned int boot_len) 
{
    unsigned int boot_crc32_cal;

    // Check length
    if ((boot_len%MEMORY_PAGE_SIZE != 0) || (boot_len > REGION_SIZE) || (boot_len == 0)) 
    {
        return ERROR;
    }

    // Recompute and check signature
    boot_crc32_cal = crc32(CRC32_CHECK_CODE,(unsigned char *)vAddress,boot_len);

    if (bootCode_check(boot_crc32, boot_crc32_cal) != OK) 
    {
        return ERROR;
    }
    else 
    {
        return OK;
    }
}*/

void boot_load_proc(void)
{
    uint8_t boot_info_valid;
    uint8_t *code_start_addr;
    uint8_t *info_start_addr;
    uint8_t code_len;

    uint32_t crc_result = 0;
    uint32_t crc_check;
    
    init_crc_table();   
    flash_init();
    
    flash_lock((void*)IFLASH0_ADDR,(void*)(IFLASH0_ADDR+BOOTLOADER_SIZE-1));
    
    //flash_read(BOOT_INFO_VALID_START_ADDRESS, boot_info_valid);
    boot_info_valid = *((uint8_t *)(BOOT_INFO_VALID_START_ADDRESS));
    
    if((boot_info_valid != 0x55) && (boot_info_valid != 0xaa))
    {
        return;
    }
    else if(boot_info_valid == 0x55)
    {
        code_start_addr = (uint8_t*)APP_A_START_ADDRESS;
        info_start_addr = BOOT_INFO_A_START_ADDRESS;
    }
    else
    {
        code_start_addr = (uint8_t*)APP_B_START_ADDRESS;
        info_start_addr = BOOT_INFO_B_START_ADDRESS;
    }
    
    code_len = *(info_start_addr);   
    crc_result = crc32(CRC32_CHECK_CODE, code_start_addr, (code_len - 1) *  512);   
    crc_check = *((uint32_t*)((uint32_t)code_start_addr + (code_len - 1) *  512));
    
    if(crc_result != crc_check)
    {
        return;
    }
    
    binary_exec((void*)code_start_addr);
}
          
void erase_valid(void)
{
    uint8_t valid_info = 0;
    if(flash_unlock(BOOT_INFO_A_START_ADDRESS, NULL) != 0)
    {
        return;
    }
    
    flash_write(BOOT_INFO_VALID_START_ADDRESS, (void*)(&valid_info));
    
    flash_lock(BOOT_INFO_A_START_ADDRESS, NULL);
}

uint8_t get_code_idx(void)
{
    uint8_t boot_info_valid;
    uint8_t ret = 0;
  
    boot_info_valid = *((uint8_t *)(BOOT_INFO_VALID_START_ADDRESS));
    
    if((boot_info_valid != 0x55) && (boot_info_valid != 0xaa))
    {
        ret = 0;
    }
    else if(boot_info_valid == 0x55)
    {
        ret = 1;
    }
    else
    {
        ret = 0;
    }
    
    return ret;
}

bool write_new_code(uint8_t idx, uint8_t zone_num, uint8_t *cod_buff)
{
    uint8_t *code_addr;
    uint8_t *code_addr_end;
    
    if(zone_num == 1)
    {
        code_addr = APP_B_START_ADDRESS;
        code_addr_end = APP_B_END_ADDRESS;
    }
    else
    {
        code_addr = APP_A_START_ADDRESS;
        code_addr_end = APP_A_END_ADDRESS;
    }
    
    if(flash_unlock(code_addr, code_addr_end) != 0)
    {
        return false;
    }
    
    if(flash_write(code_addr + idx * 2 * MEMORY_PAGE_SIZE, cod_buff) != 0)
    {
        flash_lock(APP_A_START_ADDRESS, NULL);
        return false;
    }
    
    if(flash_write(code_addr + (idx * 2 + 1) * MEMORY_PAGE_SIZE, cod_buff + MEMORY_PAGE_SIZE) != 0)
    {
        flash_lock(APP_A_START_ADDRESS, NULL);
        return false;
    }
    
    flash_lock(code_addr, code_addr_end);
    return true;
}

bool finish_write_code(uint8_t code_len, uint8_t zone_num)
{
    uint8_t *code_addr;
    uint8_t *info_addr;
    
    uint32_t crc_result = 0;
    uint32_t crc_check;
    
    uint8_t info_data = code_len;
    uint8_t valid_data;
    
    if(zone_num == 1)
    {
        code_addr = APP_B_START_ADDRESS;
        info_addr = BOOT_INFO_B_START_ADDRESS;
        valid_data = 0xaa;
    }
    else
    {
        code_addr = APP_A_START_ADDRESS;
        info_addr = BOOT_INFO_A_START_ADDRESS;
        valid_data = 0x55;
    }
    
    crc_result = crc32(CRC32_CHECK_CODE, code_addr, (code_len - 1) *  512);   
    crc_check = *((uint32_t*)((uint32_t)code_addr + (code_len - 1) *  512));
    
    if(crc_result != crc_check)
    {
        return false;
    }
    
    if(flash_unlock(BOOT_INFO_A_START_ADDRESS, NULL) != 0)
    {
        return false;
    }
    
    if(flash_write(info_addr, &info_data) != 0)
    {
        flash_lock(APP_A_START_ADDRESS, NULL);
        return false;
    }
    
    if(flash_write(BOOT_INFO_VALID_START_ADDRESS, &valid_data) != 0)
    {
        flash_lock(APP_A_START_ADDRESS, NULL);
        return false;
    }
    
    flash_lock(BOOT_INFO_A_START_ADDRESS, NULL);
    return true;
}

uint32_t get_code_crc(uint8_t code_len, uint8_t zone_num)
{
    uint8_t *code_addr;
    
    uint32_t crc_result = 0;
    
    if(zone_num == 1)
    {
        code_addr = APP_B_START_ADDRESS;
    }
    else
    {
        code_addr = APP_A_START_ADDRESS;
    }
    
    crc_result = crc32(CRC32_CHECK_CODE, code_addr, (code_len - 1) *  512);   
    
    return crc_result;
}

/*void get_code(uint8_t code_idx, uint8_t zone_num, uint8_t *pbuff)
{
    uint8_t *code_addr;
    uint16_t i;
    
    if(zone_num == 1)
    {
        code_addr = APP_B_START_ADDRESS;
    }
    else
    {
        code_addr = APP_A_START_ADDRESS;
    }
    
    common_mem_cpy(code_addr + code_idx * 512, pbuff, 512);
}*/


